package ar.edu.uade.android.mjpeg;

import java.io.IOException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MjpegView
    extends SurfaceView
    implements SurfaceHolder.Callback
{
    public final static int POSITION_UPPER_LEFT = 9;
    public final static int POSITION_UPPER_RIGHT = 3;
    public final static int POSITION_LOWER_LEFT = 12;
    public final static int POSITION_LOWER_RIGHT = 6;

    public final static int SIZE_STANDARD = 1;
    public final static int SIZE_BEST_FIT = 4;
    public final static int SIZE_FULLSCREEN = 8;

    private MjpegViewThread thread;
    private MjpegInputStream mIn = null;
    private boolean showFps = false;
    private boolean mRun = false;
    private boolean surfaceDone = false;
    private Paint overlayPaint;
    private int overlayTextColor;
    private int overlayBackgroundColor;
    private int ovlPos;
    private int dispWidth;
    private int dispHeight;
    private int displayMode;
    private Bitmap lastBitmap;
    
    public class MjpegViewThread
        extends Thread
    {
        private SurfaceHolder mSurfaceHolder;
        private int frameCounter = 0;
        private long start;
        private String fps = "";
        private Bitmap ovl;

        public MjpegViewThread( SurfaceHolder surfaceHolder, Context context )
        {
            mSurfaceHolder = surfaceHolder;
        }

        private Rect destRect( int bmw, int bmh )
        {
            int tempx;
            int tempy;
            if ( displayMode == MjpegView.SIZE_STANDARD )
            {
                tempx = ( dispWidth / 2 ) - ( bmw / 2 );
                tempy = ( dispHeight / 2 ) - ( bmh / 2 );
                return new Rect( tempx, tempy, bmw + tempx, bmh + tempy );
            }
            if ( displayMode == MjpegView.SIZE_BEST_FIT )
            {
                float bmasp = (float) bmw / (float) bmh;
                bmw = dispWidth;
                bmh = (int) ( dispWidth / bmasp );
                if ( bmh > dispHeight )
                {
                    bmh = dispHeight;
                    bmw = (int) ( dispHeight * bmasp );
                }
                tempx = ( dispWidth / 2 ) - ( bmw / 2 );
                tempy = ( dispHeight / 2 ) - ( bmh / 2 );
                return new Rect( tempx, tempy, bmw + tempx, bmh + tempy );
            }
            if ( displayMode == MjpegView.SIZE_FULLSCREEN )
                return new Rect( 0, 0, dispWidth, dispHeight );
            return null;
        }

        public void setSurfaceSize( int width, int height )
        {
            synchronized ( mSurfaceHolder )
            {
                dispWidth = width;
                dispHeight = height;
            }
        }

        private Bitmap makeFpsOverlay( Paint p )
        {
            Rect b = new Rect();
            p.getTextBounds( fps, 0, fps.length(), b );

            // false indentation to fix forum layout
            Bitmap bm = Bitmap.createBitmap( b.width()+5, b.height()+5, Bitmap.Config.ARGB_8888 );

            Canvas c = new Canvas( bm );
            p.setColor( overlayBackgroundColor );
            c.drawRect( 0, 0, b.width(), b.height(), p );
            p.setColor( overlayTextColor );
            c.drawText( fps, -b.left, b.bottom - b.top - p.descent(), p );
            return bm;
        }

        public void run()
        {
            start = System.currentTimeMillis();
            PorterDuffXfermode mode = new PorterDuffXfermode( PorterDuff.Mode.DST_OVER );
            Bitmap bm;
            int width;
            int height;
            Rect destRect;
            Canvas c = null;
            Paint p = new Paint();
            while ( mRun )
            {
                if ( surfaceDone )
                {
                    try
                    {
                        c = mSurfaceHolder.lockCanvas();
                        synchronized ( mSurfaceHolder )
                        {
                            try
                            {
                                bm = mIn.readMjpegFrame();
                                lastBitmap = bm.copy( Bitmap.Config.RGB_565, false );
                                destRect = destRect( bm.getWidth(), bm.getHeight() );
                                c.drawColor( Color.BLACK );
                                c.drawBitmap( bm, null, destRect, p );
                                if ( showFps )
                                {
                                    p.setXfermode( mode );
                                    if ( ovl != null )
                                    {
                                        height = ( ( ovlPos & 1 ) == 1 ) ? destRect.top : destRect.bottom
                                            - ovl.getHeight();
                                        width = ( ( ovlPos & 8 ) == 8 ) ? destRect.left : destRect.right
                                            - ovl.getWidth();

                                        c.drawBitmap( ovl, width, height, null );
                                    }
                                    p.setXfermode( null );
                                    frameCounter++;
                                    if ( ( System.currentTimeMillis() - start ) >= 1000 )
                                    {
                                        fps = String.valueOf( frameCounter ) + "cps";
                                        frameCounter = 0;
                                        start = System.currentTimeMillis();
                                        ovl = makeFpsOverlay( overlayPaint );
                                    }
                                }
                            }
                            catch ( IOException e )
                            {
                            }
                        }
                    }
                    finally
                    {
                        if ( c != null )
                            mSurfaceHolder.unlockCanvasAndPost( c );
                    }
                }
            }
        }
    }

    private void init( Context context )
    {
        SurfaceHolder holder = getHolder();
        holder.addCallback( this );
        thread = new MjpegViewThread( holder, context );
        setFocusable( true );
        overlayPaint = new Paint();
        overlayPaint.setTextAlign( Paint.Align.LEFT );
        overlayPaint.setTextSize( 36 );
        overlayPaint.setTypeface( Typeface.DEFAULT );
        overlayTextColor = Color.WHITE;
        overlayBackgroundColor = Color.argb( 160, 0, 0, 0 );
        ovlPos = MjpegView.POSITION_LOWER_LEFT;
        displayMode = MjpegView.SIZE_STANDARD;
        dispWidth = getWidth();
        dispHeight = getHeight();
    }

    public void startPlayback()
    {
        if ( mIn != null )
        {
            mRun = true;
            thread.start();
        }
    }

    public void stopPlayback()
    {
        mRun = false;
        boolean retry = true;
        while ( retry )
        {
            try
            {
                thread.join();
                retry = false;
            }
            catch ( InterruptedException e )
            {
            }
        }
    }

    public MjpegView( Context context, AttributeSet attrs )
    {
        super( context, attrs );
        init( context );
    }

    public void surfaceChanged( SurfaceHolder holder, int f, int w, int h )
    {
        thread.setSurfaceSize( w, h );
    }

    public void surfaceDestroyed( SurfaceHolder holder )
    {
        surfaceDone = false;
        stopPlayback();
    }

    public MjpegView( Context context )
    {
        super( context );
        init( context );
    }

    public void surfaceCreated( SurfaceHolder holder )
    {
        surfaceDone = true;
    }

    public void showFps( boolean b )
    {
        showFps = b;
    }

    public void setSource( MjpegInputStream source )
    {
        mIn = source;
        startPlayback();
    }

    public void setOverlayPaint( Paint p )
    {
        overlayPaint = p;
    }

    public void setOverlayTextColor( int c )
    {
        overlayTextColor = c;
    }

    public void setOverlayBackgroundColor( int c )
    {
        overlayBackgroundColor = c;
    }

    public void setOverlayPosition( int p )
    {
        ovlPos = p;
    }

    public void setDisplayMode( int s )
    {
        displayMode = s;
    }

    public Bitmap getLastFrame()
    {
        return lastBitmap;
    }

}